<?php

// +----------------------------------------------------------------------
// | H1CMS © OpenSource CMS
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2016 http://www.h1cms.com All rights reserved.
// | Copyright (c) 2014-2016 嘉兴领格信息技术有限公司，并保留所有权利。
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Allen <allen@lg4.cn>
// +----------------------------------------------------------------------

namespace h1cms\system;

use ArrayAccess;
use Lg\Database\Query;

/**
 * Description of Model
 * 
 * @method Lg\Database\Query table(string $table) Query Builer
 *
 * @author Allen <allen@lg4.cn>
 */
class Model implements ArrayAccess {

    /**
     *
     * @var string tablename
     */
    protected $table;

    /**
     *
     * @var string primarykey
     */
    protected $primaryKey = 'id';

    /**
     *
     * @var array db field
     */
    protected $attributes = array();

    /**
     *
     * @var boolean newrecord
     */
    public $isNewRecord = false;

    /**
     * Table Attributes
     * @param array $attributes
     */
    public function __construct(array $attributes = array()) {
        $this->fill($attributes);
    }

    /**
     * Set New Record
     * @param boolean $flag
     * @return \h1cms\system\Model
     */
    public function setNewRecord($flag) {
        $this->isNewRecord = $flag;
        return $this;
    }

    /**
     * Get TableName or Class Basename
     * @return string Table Name
     */
    public function getTable() {
        if (isset($this->table)) {
            return $this->table;
        }
    }

    /**
     * Set table name
     * @param string $table
     * @return \h1cms\system\Model
     */
    public function setTable($table) {
        $this->table = $table;
        return $this;
    }

    /**
     * Get PrimaryKey Name
     * @return string
     */
    public function getPrimaryKeyName() {
        return $this->primaryKey;
    }

    /**
     * Set PrimaryKey
     * @param string $key
     * @return \h1cms\system\Model
     */
    public function setPrimaryKeyName($key) {
        $this->primaryKey = $key;
        return $this;
    }

    /**
     * 
     * @return int PrimaryKey Value
     */
    public function getPrimaryKeyValue() {
        return $this->getAttribute($this->getPrimaryKeyName());
    }

    /**
     * 
     * @return \h1cms\system\Model
     */
    public function save() {
        if ($this->isNewRecord) {
            static::getDb()->insert($this->getTable(), $this->getAttributes());
            $this->setNewRecord(false);
            $this->setAttribute($this->getPrimaryKeyName(), static::getDb()->lastId());
            return $this->getPrimaryKeyValue();
        } else {
            return static::getDb()->update($this->getTable(), $this->getAttributes(), [$this->getPrimaryKeyName() => $this->getPrimaryKeyValue()]);
        }
    }

    /**
     * Update Table
     * @param array $attributes
     * @return \h1cms\system\Model
     */
    public function update(array $attributes = array()) {
        if ($this->isNewRecord) {
            return static::getDb()->insert($this->getTable(), $attributes);
        }
        $this->fill($attributes)->save();
        return $this;
    }

    /**
     * fill attributes
     * @param array $attributes
     * @return \h1cms\system\Model
     */
    public function fill(array $attributes = array()) {
        foreach ($attributes as $key => $value) {
            $this->attributes[$key] = $value;
        }
        return $this;
    }

    public function destory() {
        $id = $this->getPrimaryKeyValue();
        if (!$id) {
            return false;
        }
        $pkey = $this->getPrimaryKeyName();
        return Query::table($this->getTable())->delete([$pkey => $id]);
    }

    /**
     * 
     * @param array|int $ids
     * @return \h1cms\system\Model|array
     */
    public static function find($ids) {
        $ids = is_array($ids) ? $ids : func_get_args();
        $model = new static;
        $query = Query::table($model->getTable());
        $query->whereIn($model->getPrimaryKeyName(), $ids);
        return $query->fetchAll(\PDO::FETCH_CLASS, get_called_class());
    }

    /**
     * 
     * @return \h1cms\system\Model
     */
    public static function all() {
        $model = new static;
        $query = Query::table($model->getTable());
        return $query->fetchAll(\PDO::FETCH_CLASS, get_called_class());
    }

    /**
     * 
     * @param array $params
     * @return \h1cms\system\Model
     */
    public static function findAll($params = array()) {
        $model = new static;
        $query = Query::table($model->getTable());
        if (isset($params['pagesize'])) {
            $offset = isset($params['offset']) ? intval($params['offset']) : 0;
            $query->limit(intval($params['pagesize']), $offset);
            unset($params['pagesize']);
            unset($params['offset']);
        }
        if (is_array($params) && !empty($params)) {
            foreach ($params as $key => $value) {
                $query->where($key, $value);
            }
        }
        return $query->fetchAll(\PDO::FETCH_CLASS, get_called_class());
    }

    /**
     * 
     * @param type $params
     * @return \h1cms\system\Model
     */
    public static function findOne($params = array()) {
        $model = new static;
        $query = $model->table($model->table);
        if (is_array($params) && !empty($params)) {
            foreach ($params as $key => $value) {
                $query->where($key, $value);
            }
        }
        return $query->fetchObject(get_called_class());
    }

    public static function count($filters = []) {
        return static::getDb()->count(static::model()->table, $filters);
    }

    public static function model() {
        return new static;
    }

    /**
     * 
     * @param int|array $ids
     * @return type
     */
    public static function delete($ids) {
        $ids = is_array($ids) ? $ids : func_get_args();
        $model = new static;
        return $model->whereIn($model->getPrimaryKeyName(), $ids)->delete();
    }

    /**
     * 
     * @param array $attributes
     * @return \static
     */
    public static function create(array $attributes = array()) {
        $model = new static($attributes);
        $model->setNewRecord(true);
        $model->save();
        return $model;
    }

    /**
     * @return \Lg\Database\Database
     */
    public static function getDb($target = null) {
        return \Lg\Database\Database::getConnection($target);
    }

    public function setAttribute($key, $value) {
        $this->attributes[$key] = $value;
        return $this;
    }

    public function hasAttribute($key) {
        return isset($this->attributes[$key]);
    }

    public function getAttribute($key, $default = null) {
        if ($this->hasAttribute($key)) {
            return $this->attributes[$key];
        }
        return $default;
    }

    public function removeAttribute($key) {
        unset($this->attributes[$key]);
        return $this;
    }

    public function getAttributes() {
        return $this->attributes;
    }

    public function toJson() {
        return json_encode($this->attributes);
    }

    public function toXML() {
        $class = strtolower(class_basename(get_called_class()));
        return xmlEncode(array($class => $this->attributes));
    }

    /**
     * Dynamically retrieve attributes on the model.
     *
     * @param  string  $key
     * @return mixed
     */
    public function __get($key) {
        return $this->getAttribute($key);
    }

    /**
     * Dynamically set attributes on the model.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return void
     */
    public function __set($key, $value) {
        $this->setAttribute($key, $value);
    }

    /**
     * Determine if the given attribute exists.
     *
     * @param  mixed  $offset
     * @return bool
     */
    public function offsetExists($offset) {
        return $this->hasAttribute($offset);
    }

    /**
     * Get the value for a given offset.
     *
     * @param  mixed  $offset
     * @return mixed
     */
    public function offsetGet($offset) {
        return $this->getAttribute($offset);
    }

    /**
     * Set the value for a given offset.
     *
     * @param  mixed  $offset
     * @param  mixed  $value
     * @return void
     */
    public function offsetSet($offset, $value) {
        $this->setAttribute($offset, $value);
    }

    /**
     * Unset the value for a given offset.
     *
     * @param  mixed  $offset
     * @return void
     */
    public function offsetUnset($offset) {
        $this->removeAttribute($offset);
    }

    /**
     * Determine if an attribute exists on the model.
     *
     * @param  string  $key
     * @return bool
     */
    public function __isset($key) {
        return isset($this->attributes[$key]);
    }

    /**
     * Unset an attribute on the model.
     *
     * @param  string  $key
     * @return void
     */
    public function __unset($key) {
        unset($this->attributes[$key]);
    }

    /**
     * 
     * @param type $method
     * @param type $parameters
     * @return \hmvc\Database\Query
     */
    public function __call($method, $parameters) {
        $query = Query::table($this->getTable());
        return call_user_func_array(array($query, $method), $parameters);
    }

    /**
     * Handle dynamic static method calls into the method.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public static function __callStatic($method, $parameters) {
        $instance = new static;
        return call_user_func_array(array($instance, $method), $parameters);
    }

    /**
     * 
     * @param int $id
     * @return \h1cms\system\Model
     */
    public static function findById($id) {
        $model = new static;
        $query = $model->where($model->getPrimaryKeyName(), $id);
        return $query->fetchObject(get_called_class());
    }

}
